
#include "RtBase.h"
#include "RtChannelHttpServer.h"
#include "RtMessageBlock.h"
#include "RtHttpUrl.h"

#define CONTENT_TYPE_UPLOAD_FILE "multipart/form-data"
#define CONTENT_TYPE_PARAM_BOUNDARY "boundary"

CRtChannelHttpServer::CRtChannelHttpServer(IRtTransport *aTrpt)
	: CRtChannelHttpBase(this)
	, m_ParserRequest(m_RequestHeader, this)
	, m_response_data(NULL)
{
	m_pTransport = aTrpt;
	RT_ASSERTE(m_pTransport);
	m_bOpenWith = FALSE;
}

CRtChannelHttpServer::~CRtChannelHttpServer()
{
	Disconnect(RT_OK);
	if(m_response_data)
		m_response_data->DestroyChained();
	m_response_data = NULL;
}

DWORD CRtChannelHttpServer::AddReference()
{
	return CRtReferenceControlSingleThreadTimerDelete::AddReference();
}

DWORD CRtChannelHttpServer::ReleaseReference()
{
	return CRtReferenceControlSingleThreadTimerDelete::ReleaseReference();
}

RtResult CRtChannelHttpServer::OpenWithSink(IRtTransportSink *aSink)
{
	//RT_ASSERTE(!m_pSink);
	//RT_ASSERTE_RETURN(aSink, RT_ERROR_INVALID_ARG);
	m_pSink = aSink;
	if(m_bOpenWith)
		return RT_OK;
	m_bOpenWith = TRUE;
	SetResponseStatus(200, "OK");
	m_ResponseHeader.SetVerion(HttpVersion_1_1);
	return m_pTransport->OpenWithSink(this);
}

IRtTransportSink* CRtChannelHttpServer::GetSink()
{
	return m_pSink;
}

RtResult CRtChannelHttpServer::SendData(
		CRtMessageBlock& inData,
		BOOL inSendImmediately,
		BOOL inDestroyData,
		DataPriority inPriority, 
		BOOL inNeedAck)
{
	RtResult rv = RT_OK;
	RT_ASSERTE_RETURN(m_pTransport, RT_ERROR_NOT_INITIALIZED);

	if (m_bIsFirstSend) {
		if (!m_customContentLength) {
			rv = SetContentLength_i(m_ResponseHeader, &inData);
			if (RT_FAILED(rv))
				return rv;
		}

		CRtString strResponse = m_ResponseHeader.Flatten();
		strResponse += CRtHttpHeaderArray::s_szHttpHeaderNewLine;

		CRtMessageBlock mbHead(
			strResponse.length(),
			const_cast<LPSTR>(strResponse.c_str()),
			CRtMessageBlock::DONT_DELETE,
			strResponse.length());
// 		if (inData.GetChainedLength() > 0)
			mbHead.Append(&inData);
		
		rv = SendData_i(mbHead);
		if (RT_FAILED(rv)) {
			RT_ERROR_TRACE("CRtChannelHttpServer::SendPrivateData, it's impossible that sending first packet failed!");
			return rv;
		}
		if(m_bStream)
			m_bIsFirstSend = FALSE;
	}
	else if (inData.GetChainedLength() > 0) {
		rv = SendData_i(inData);
	}
	else {
		RT_ASSERTE(FALSE);
		rv = RT_ERROR_INVALID_ARG;
	}

	if (RT_SUCCEEDED(rv) && inDestroyData)
		inData.DestroyChained();

	return rv;
}

RtResult CRtChannelHttpServer::SetOption(DWORD aCommand, LPVOID aArg)
{
	switch(aCommand) {
	case RT_OPT_CHANNEL_HTTP_PARSER_SKIP_CONTENT_LENGTH:
		m_ParserRequest.m_bSkipContentLength = *static_cast<BOOL*>(aArg);
		return RT_OK;
	case RT_OPT_CHANNEL_HTTP_MAX_CONTENT_LENGTH:
		m_ResponseHeader.SetMaxContentLength(*static_cast<LONG*>(aArg));
		return RT_OK;

	default:
		return CRtChannelHttpBase::SetOption_i(aCommand, aArg);
	}
}

RtResult CRtChannelHttpServer::GetOption(DWORD aCommand, LPVOID aArg)
{
	switch(aCommand) {
	case RT_OPT_CHANNEL_HTTP_PARSER_SKIP_CONTENT_LENGTH:
		*static_cast<BOOL*>(aArg) = m_ParserRequest.m_bSkipContentLength;
		return RT_OK;
	case RT_OPT_CHANNEL_HTTP_MAX_CONTENT_LENGTH:
		*static_cast<LONG*>(aArg) = m_ResponseHeader.GetMaxContentLength();
		return RT_OK;
		
	default:
		return CRtChannelHttpBase::GetOption_i(aCommand, aArg);
	}
}

RtResult CRtChannelHttpServer::Disconnect(RtResult aReason)
{
	return Close_t(aReason);
}

RtResult CRtChannelHttpServer::GetRequestPath(CRtString &aPath)
{
	aPath = m_RequestHeader.GetRequestURI();
	return aPath.empty() ? RT_ERROR_FAILURE : RT_OK;
}

RtResult CRtChannelHttpServer::GetUploadFileInfo(
		CRtMessageBlock& inData, 
		const CRtString& inInputTagName, 
		CRtString& outFileName, 
		CRtString& outFileContent)
{
	/// Get boundary string. 

	CRtString contentType;
	m_RequestHeader.GetHeader(CRtHttpAtomList::Content_Type, contentType);
	size_t lenContentType = strlen(CONTENT_TYPE_UPLOAD_FILE);
	if ( contentType.length() < lenContentType || 
		strncasecmp(contentType.c_str(), CONTENT_TYPE_UPLOAD_FILE, lenContentType) )
	{
		RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, invalid Content-Type:"<<contentType);
		return RT_ERROR_FAILURE;
	}
	CRtString::size_type pos = contentType.find(CONTENT_TYPE_PARAM_BOUNDARY);
	if (pos==contentType.npos) {
		RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, boundary param not found!");
		return RT_ERROR_FAILURE;
	}
	pos += 9; // Pass through "boundary=".
	CRtString strBoundary(&contentType[pos]);

	/// Find out input tag name. 

	CRtString strData = inData.FlattenChained();
	CRtString::size_type dataLength = strData.length();
	CRtString::size_type dataOffset = 0;
	do 
	{
		pos = strData.find("name=\"", dataOffset);
		if (pos==strData.npos) {
			RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, 'name=\"' not found!");
			return RT_ERROR_FAILURE;
		}
		dataOffset = pos + 6;
		if (dataLength-dataOffset < inInputTagName.length())
		{
			RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, no enough data!");
			return RT_ERROR_FAILURE;
		}
		if (strData[dataOffset+inInputTagName.length()]=='\"' &&
			strncasecmp(&strData[dataOffset], inInputTagName.c_str(), inInputTagName.length())==0)
		{
			// Got it.
			break;
		}

		pos = strData.find(strBoundary, dataOffset);
		if (pos==strData.npos) {
			RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, beginning boundary string not found!");
			return RT_ERROR_FAILURE;
		}
		dataOffset = pos + strBoundary.length();
	} while(1);

	/// Get file name.

	pos = strData.find("filename=\"", dataOffset);
	if (pos==strData.npos) {
		RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, 'filename=\"' not found!");
	}
	else {
		dataOffset = pos + 10;
		pos = strData.find('\"', dataOffset);
		if (pos==strData.npos) {
			RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, '\"' not found!");
			return RT_ERROR_FAILURE;
		}
		CRtString::size_type posTmp = strData.find_last_of('\\', pos-1);
		if (posTmp==strData.npos || posTmp<dataOffset)
			posTmp = strData.find_last_of('/', pos-1);
		if (posTmp!=strData.npos && posTmp>=dataOffset)
			dataOffset = posTmp + 1;
		outFileName.assign(strData, dataOffset, pos - dataOffset);
		dataOffset = pos + 1;
	}

	/// Get file content.

	pos = strData.find("\r\n\r\n", dataOffset);
	if (pos==strData.npos) {
		RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, double 'cr-ln' not found!");
		return RT_ERROR_FAILURE;
	}
	dataOffset = pos + 4;
	pos = strData.find(strBoundary, dataOffset);
	if (pos==strData.npos) {
		RT_WARNING_TRACE_THIS("CRtChannelHttpServer::GetUploadFileInfo, ending boundary string not found!");
		return RT_ERROR_FAILURE;
	}
	pos -= 4; // Backspace "\r\n--"
	outFileContent.assign(strData, dataOffset, pos - dataOffset);

	return RT_OK;
}

RtResult CRtChannelHttpServer::
SetOrAddResponseHeader(const CRtString &aHeader, const CRtString &aValue)
{
	CRtHttpAtom atHead = CRtHttpAtomList::Instance()->ResolveAtom(aHeader);
	RT_ASSERTE_RETURN(atHead.get(), RT_ERROR_NOT_FOUND);

	BOOL bRet = m_ResponseHeader.SetHeader(atHead, aValue);
	return bRet ? RT_OK : RT_ERROR_FAILURE;
}

RtResult CRtChannelHttpServer::
SetResponseStatus(DWORD aStatus, const CRtString &aText)
{
	m_ResponseHeader.SetStatus(aStatus);
	m_ResponseHeader.SetStatusText(aText);
	return RT_OK;
}

RtResult CRtChannelHttpServer::GetRequestMethod(CRtString &aMethod)
{
	aMethod = m_RequestHeader.GetMethod().get();
	return !aMethod.empty() ? RT_OK : RT_ERROR_FAILURE;
}

RtResult CRtChannelHttpServer::
GetRequestHeader(const CRtString &aHeader, CRtString &aValue)
{
	CRtHttpAtom atHead = CRtHttpAtomList::Instance()->ResolveAtom(aHeader);
	RT_ASSERTE_RETURN(atHead.get(), RT_ERROR_NOT_FOUND);
	
	BOOL bRet = m_RequestHeader.GetHeader(atHead, aValue);
	return bRet ? RT_OK : RT_ERROR_FAILURE;
}

RtResult CRtChannelHttpServer::PrepareResponse(CRtMessageBlock& data)
{
	if(data.GetChainedLength()==0)
		return RT_OK;
	if(m_response_data)
		m_response_data->Append(data.DuplicateChained());
	else
		m_response_data = data.DuplicateChained();
	return RT_OK;
}

RtResult CRtChannelHttpServer::Response()
{
	RtResult ret;
	if(m_response_data)
	{
		ret = SendData(*m_response_data);
		m_response_data->DestroyChained();
		m_response_data = NULL;
	}
	else
	{
		CRtMessageBlock data(0UL);
		ret = SendData(data);
	}
	return ret;
}

RtResult CRtChannelHttpServer::
ParserHttpProtocol_t(CRtMessageBlock &aData)
{
	if(m_bFullResponse)
	{
		m_bFullResponse = FALSE;
		m_ParserRequest.Clear();
	}

	RtResult rv = RT_OK;
	if (!m_ParserRequest.IsFinished()) {
		rv = m_ParserRequest.Analyse(aData);
		if (RT_SUCCEEDED(rv)) {
			RT_ASSERTE(m_ParserRequest.IsFinished());
			CRtMessageBlock *pMb = NULL;
			rv = m_ParserRequest.GetContent(pMb);
			if (RT_SUCCEEDED(rv)) {
				CRtMessageBlock mbTmp(0UL);
				if (!pMb)
					pMb = &mbTmp;

				RT_ASSERTE(m_pSink);
				if (m_pSink)
					m_pSink->OnReceive(*pMb, this);

				if (pMb != &mbTmp)
					pMb->DestroyChained();
			}
		}
	}
	return rv;
}

RtResult CRtChannelHttpServer::Close_t(RtResult aReason)
{
	m_pURL = NULL;
	return CRtChannelHttpBase::Close_t(aReason);
}

IRtTransport* CRtChannelHttpServer::GetTcpTransport()
{
	return m_pTransport.Get();
}
